/*
 *  Copyright 2013 National Institute of Advanced Industrial Science and Technology
 *  
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package org.sss.server;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

import org.slf4j.Logger;
import org.sss.util.Util;

/**
 * Class loader from byte-string on memory.
 */
public class BytesClassLoader extends ClassLoader {
  static private Logger logger = SssServer.getLogger(BytesClassLoader.class);
  private Map<String, byte[]> cache = new HashMap<String, byte[]>();
  private URLStreamHandler byteJarURLStreamHandler = new ByteJarURLStreamHandler();

  /**
   * Appends jar file data.
   */
  public synchronized void appendJar(byte[] jardata) throws IOException {
    JarInputStream jis = new JarInputStream(new ByteArrayInputStream(jardata));
    JarEntry entry;
    while ((entry = jis.getNextJarEntry()) != null) {
      cache.put(entry.getName(), Util.readAll(jis));
    }
  }

  @Override
  protected synchronized URL findResource(String name){
    try {
      if (!cache.containsKey(name)) {
        return null;
      }
      return new URL("byteJar", "localhost", 0, name, byteJarURLStreamHandler);
    }
    catch (MalformedURLException e) {
      return null;
    }
  }

  @Override
  protected Class<?> findClass(String className) throws ClassNotFoundException {
	  byte[] byteCode = cache.get(className.replace(".", "/") + ".class");
    if (byteCode == null) {
		logger.error("cannot find class for " + className + " , what I have are..");
		for (String key: cache.keySet())    	
    		logger.error("-- " + key);
    	throw new ClassNotFoundException("cannot find class for " + className);
    }
    return defineClass(className, byteCode, 0, byteCode.length);
  }

  private class ByteURLConnection extends URLConnection {
    byte [] bytes;
    public ByteURLConnection(URL url, byte [] bytes) {
      super(url);
      this.bytes = bytes;
    }
    @Override
    public void connect() throws IOException {
      // nothing to do;
    }
    @Override
    public InputStream getInputStream() {
      return new ByteArrayInputStream(bytes);
    }
  }

  private class ByteJarURLStreamHandler extends URLStreamHandler {
    @Override
    protected URLConnection openConnection(URL url) throws IOException {
      byte [] bytes = cache.get(url.getPath());
      if (bytes == null) {
        throw new IOException("failed to open the path");
      }
      return new ByteURLConnection(url, bytes);
    }
  }
}
